package com.patrick.open.api.core.utils;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.patrick.open.api.core.common.OpenTkMap;
import com.patrick.open.api.core.common.RespData;
import org.springframework.http.MediaType;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author GP
 * @date 2019/10/21 10:02 AM
 */
public class JsonGzp {
  /**
   * 默认转换深度
   */
  private static int defaultConvertDepth = 15;
  /**
   * 默认日期格式
   */
  private static String defaultDatePattern = "yyyy-MM-dd HH:mm:ss";
  /**
   * 默认时间格式
   */
  private static String defaultTimestampPattern = "yyyy-MM-dd HH:mm:ss";

  private JsonGzp() {
  }

  /**
   * 将对象转换为Json字符串
   *
   * @param object 要转换的对象
   * @return 转换后的Json字符串
   */
  public static String toJson(Object object) {
    return toJson(object, null, null, defaultConvertDepth);
  }

  /**
   * 将对象转换为Json字符串
   *
   * @param object           要转换的对象
   * @param datePattern      日期格式
   * @param timestampPattern 时间格式
   * @return 转换后的Json字符串
   */
  public static String toJson(Object object, String datePattern, String timestampPattern) {
    return toJson(object, datePattern, timestampPattern, defaultConvertDepth);
  }

  /**
   * 将Json字符串解析为对象
   *
   * @param jsonString Json字符串
   * @param type       对象类型
   * @param <T>        对象类型
   * @return 解析后的对象
   */
  public static <T> T parse(String jsonString, Class<T> type) {
    ObjectMapper mapper = new ObjectMapper();
    try {
      T obj = mapper.readValue(jsonString, type);
      if (obj instanceof RespData) {
        ((RespData) obj).code(((RespData) obj).getCode());
        ((RespData) obj).msg(((RespData) obj).getMsg());
      }
      return obj;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * 格式化JSON
   *
   * @param jsonStr JSON字符串
   * @return
   */
  public static String formatJson(String jsonStr) {
    if (null == jsonStr || "".equals(jsonStr)) {
      return "";
    }
    StringBuilder sb = new StringBuilder();
    char last = '\0';
    char current = '\0';
    int indent = 0;
    boolean isInQuotationMarks = false;
    for (int i = 0; i < jsonStr.length(); i++) {
      last = current;
      current = jsonStr.charAt(i);
      switch (current) {
        case '"':
          if (last != '\\') {
            isInQuotationMarks = !isInQuotationMarks;
          }
          sb.append(current);
          break;
        case '{':
        case '[':
          sb.append(current);
          if (!isInQuotationMarks) {
            sb.append('\n');
            indent++;
            addIndentBlank(sb, indent);
          }
          break;
        case '}':
        case ']':
          if (!isInQuotationMarks) {
            sb.append('\n');
            indent--;
            addIndentBlank(sb, indent);
          }
          sb.append(current);
          break;
        case ',':
          sb.append(current);
          if (last != '\\' && !isInQuotationMarks) {
            sb.append('\n');
            addIndentBlank(sb, indent);
          }
          break;
        default:
          sb.append(current);
      }
    }

    return sb.toString();
  }

  /**
   * 添加space
   *
   * @param sb
   */
  private static void addIndentBlank(StringBuilder sb, int indent) {
    for (int i = 0; i < indent; i++) {
      sb.append('\t');
    }
  }

  protected static String mapToJson(Map map, String datePattern, String timestampPattern, int depth) {
    if (map == null) {
      return "null";
    } else {
      StringBuilder sb = new StringBuilder();
      boolean first = true;
      Iterator iter = map.entrySet().iterator();
      sb.append('{');

      while (iter.hasNext()) {
        if (first) {
          first = false;
        } else {
          sb.append(',');
        }

        Map.Entry entry = (Map.Entry) iter.next();
        toKeyValue(String.valueOf(entry.getKey()), entry.getValue(), sb, datePattern, timestampPattern, depth);
      }
      sb.append('}');
      return sb.toString();
    }
  }

  protected static String toKeyValue(String key, Object value, StringBuilder sb, String datePattern, String timestampPattern, int depth) {
    sb.append('"');
    if (key == null) {
      sb.append("null");
    } else {
      escape(key, sb);
    }

    sb.append('"').append(':');
    sb.append(toJson(value, datePattern, timestampPattern, depth));
    return sb.toString();
  }

  protected static String iteratorToJson(Iterator iter, String datePattern, String timestampPattern, int depth) {
    boolean first = true;
    StringBuilder sb = new StringBuilder();
    sb.append('[');

    while (iter.hasNext()) {
      if (first) {
        first = false;
      } else {
        sb.append(',');
      }

      Object value = iter.next();
      if (value == null) {
        sb.append("null");
      } else {
        sb.append(toJson(value, datePattern, timestampPattern, depth));
      }
    }

    sb.append(']');
    return sb.toString();
  }

  protected static String escape(String s) {
    if (s == null) {
      return null;
    } else {
      StringBuilder sb = new StringBuilder();
      escape(s, sb);
      return sb.toString();
    }
  }

  protected static void escape(String s, StringBuilder sb) {
    for (int i = 0; i < s.length(); ++i) {
      char ch = s.charAt(i);
      switch (ch) {
        case '\b':
          sb.append("\\b");
          break;
        case '\t':
          sb.append("\\t");
          break;
        case '\n':
          sb.append("\\n");
          break;
        case '\f':
          sb.append("\\f");
          break;
        case '\r':
          sb.append("\\r");
          break;
        case '"':
          sb.append("\\\"");
          break;
        case '\\':
          sb.append("\\\\");
          break;
        default:
          break;
      }

      boolean b = ch >= 0 && ch <= 31 || ch >= 127 && ch <= 159 || ch >= 8192 && ch <= 8447;
      if (b) {
        String str = Integer.toHexString(ch);
        sb.append("\\u");

        for (int k = 0; k < 4 - str.length(); ++k) {
          sb.append('0');
        }

        sb.append(str.toUpperCase());
      } else {
        sb.append(ch);
      }
    }

  }

  /**
   * 自定义转换json
   *
   * @param value
   * @param datePattern
   * @param timestampPattern
   * @param depth
   * @return
   */
  protected static String toJson(Object value, String datePattern, String timestampPattern, int depth) {
    if (value != null && depth-- >= 0) {
      if (value instanceof String) {
        return "\"" + escape((String) value) + "\"";
      } else if (value instanceof Double) {
        return !((Double) value).isInfinite() && !((Double) value).isNaN() ? value.toString() : "null";
      } else if (value instanceof Float) {
        return !((Float) value).isInfinite() && !((Float) value).isNaN() ? value.toString() : "null";
      } else if (value instanceof Number) {
        return value.toString();
      } else if (value instanceof Boolean) {
        return value.toString();
      } else {
        String result;
        if (value instanceof Date) {
          if (value instanceof Timestamp) {
//						return "\"" + (new SimpleDateFormat(timestampPattern)).format(value) + "\"";
            result = timestampPattern != null ? timestampPattern : defaultTimestampPattern;
            return result != null ? "\"" + (new SimpleDateFormat(result)).format(value) + "\"" : "" + ((Date) value).getTime();
          } else if (value instanceof Time) {
            return "\"" + value.toString() + "\"";
          } else {
            result = datePattern != null ? datePattern : defaultDatePattern;
            return result != null ? "\"" + (new SimpleDateFormat(result)).format(value) + "\"" : "" + ((Date) value).getTime();
          }
        } else if (value instanceof OpenTkMap) {
          return mapToJson((Map) value, datePattern, timestampPattern, depth);
        } else if (value instanceof PaginationInterceptor) {
          return beanToJson(value, datePattern, timestampPattern, depth);
        } else if (value instanceof Collection) {
          return iteratorToJson(((Collection) value).iterator(), datePattern, timestampPattern, depth);
        } else if (value instanceof Map) {
          return mapToJson((Map) value, datePattern, timestampPattern, depth);
        } else {
          result = otherToJson(value, datePattern, timestampPattern, depth);
          return result != null ? result : "\"" + escape(value.toString()) + "\"";
        }
      }
    } else {
      return "null";
    }
  }

  /**
   * 深度转换json
   *
   * @param value
   * @param datePattern
   * @param timestampPattern
   * @param depth
   * @return
   */
  protected static String otherToJson(Object value, String datePattern, String timestampPattern, int depth) {
    if (value instanceof Character) {
      return "\"" + escape(value.toString()) + "\"";
    } else {
      if (!value.getClass().isArray()) {
        if (value instanceof Iterator) {
          return iteratorToJson((Iterator) value, datePattern, timestampPattern, depth);
        } else if (value instanceof Enumeration) {
          ArrayList<?> list = Collections.list((Enumeration) value);
          return iteratorToJson(list.iterator(), datePattern, timestampPattern, depth);
        } else {
          return value instanceof Enum ? "\"" + ((Enum) value).toString() + "\"" : beanToJson(value, datePattern, timestampPattern, depth);
        }
      } else {
        int len = Array.getLength(value);
        List<Object> list = new ArrayList(len);

        for (int i = 0; i < len; ++i) {
          list.add(Array.get(value, i));
        }

        return iteratorToJson(list.iterator(), datePattern, timestampPattern, depth);
      }
    }
  }

  /**
   * bean=>json
   *
   * @param model
   * @param datePattern
   * @param timestampPattern
   * @param depth
   * @return
   */
  protected static String beanToJson(Object model, String datePattern, String timestampPattern, int depth) {
    Map map = new HashMap(16);
    Method[] methods = model.getClass().getMethods();
    Method[] arr = methods;
    int len = methods.length;

    for (int i = 0; i < len; ++i) {
      Method m = arr[i];
      String methodName = m.getName();
      int indexOfGet = methodName.indexOf("get");
      if (indexOfGet == 0 && methodName.length() > 3) {
        String attrName = methodName.substring(3);
        if (!"Class".equals(attrName)) {
          Class<?>[] types = m.getParameterTypes();
          attrToMap(model, map, m, attrName, types);
        }
      } else {
        int indexOfIs = methodName.indexOf("is");
        if (indexOfIs == 0 && methodName.length() > 2) {
          String attrName = methodName.substring(2);
          Class<?>[] types = m.getParameterTypes();
          attrToMap(model, map, m, attrName, types);
        }
      }
    }

    return mapToJson(map, datePattern, timestampPattern, depth);
  }

  /**
   * 自定义属性转换map
   *
   * @param model
   * @param map
   * @param m
   * @param attrName
   * @param types
   */
  private static void attrToMap(Object model, Map map, Method m, String attrName, Class<?>[] types) {
    if (types.length == 0) {
      try {
        Object value = m.invoke(model);
        map.put(StrGzp.firstCharToLowerCase(attrName), value);
        map.put(attrName, value);
      } catch (Exception var15) {
        throw new RuntimeException(var15.getMessage(), var15);
      }
    }
  }

  /**
   * 客户端返回JSON字符串
   *
   * @param response
   * @param object
   * @return
   */
  public static void writeJson(HttpServletResponse response, Object object) {
    //spring5.2以后 MediaType.APPLICATION_JSON_UTF8_VALUE 已经被APPLICATION_JSON_VALUE替换 浏览器自动是别
    writeJson(response, JSON.toJSONString(object), MediaType.APPLICATION_JSON_VALUE);
  }

  /**
   * 客户端返回字符串
   *
   * @param response
   * @param string
   * @return
   */
  public static void writeJson(HttpServletResponse response, String string, String type) {
    try {
      response.setContentType(type);
      response.setCharacterEncoding("utf-8");
      response.getWriter().print(string);
      response.getWriter().flush();
      response.getWriter().close();
    } catch (IOException e) {
    }
  }
}
